package com.gitee.beiding.cloud.easy_call.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.gitee.beiding.cloud.easy_call.annotation.EasyCall;
import com.gitee.beiding.cloud.easy_call.entity.InvokeWrapper;
import com.gitee.beiding.cloud.easy_call.entity.ResultWrapper;
import com.gitee.beiding.cloud.easy_call.entity.TypeMapper;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

class CallCglib<T> implements MethodInterceptor {

    private static ObjectMapper objectMapper = new ObjectMapper();

    //使用动态代理创建一个对象
    static <T> CallCglib<T> getCglibProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        EasyCall annotation = clazz.getAnnotation(EasyCall.class);
        String name = annotation.name().trim();
        if (name.equals("")) {
            name = clazz.getName();
        }
        CallCglib<T> cglib = new CallCglib<>();

        cglib.name = name;

        String url = annotation.url().trim();
        if (!url.equals("")) {
            cglib.url = url + "/easy-call";
        } else {
            String service = annotation.service().trim();
            if (service.equals("")) {
                throw new RuntimeException("无法创建实例,使用@EasyCall请指定service或url:" + clazz.getName());
            }
            cglib.url = "http://" + service + "/easy-call";
        }

        cglib.typeMapperName = annotation.typeMapper();

        enhancer.setCallback(cglib);// 设置回调
        cglib.instance = (T) enhancer.create();
        return cglib;
    }


    private T instance;

    T getInstance() {
        return instance;
    }

    //调用的Bean名称
    private String name;
    private TypeMapper typeMapper;
    private String typeMapperName;
    private RestTemplate restTemplate;
    private String url;

    String getName() {
        return name;
    }

    void init(ApplicationContext applicationContext) {
        this.typeMapper = applicationContext.getBean(typeMapperName, TypeMapper.class);
        this.restTemplate = applicationContext.getBean("serviceRequester", RestTemplate.class);
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        InvokeWrapper invokeWrapper = new InvokeWrapper();
        invokeWrapper.setName(name);
        invokeWrapper.setMethodName(method.getName());
        Class<?>[] types = method.getParameterTypes();

        if (types.length > 0) {
            List<String> paramTypeNames = new ArrayList<>();
            List<String> paramJsonObjects = new ArrayList<>();
            for (int i = 0; i < types.length; i++) {
                Class t = types[i];
                //参数类型
                String tn = typeMapper.getNameByClass(t.getName());
                if (tn == null) {
                    tn = t.getName();
                }
                paramTypeNames.add(tn);
                paramJsonObjects.add(objectMapper.writeValueAsString(objects[i]));



            }
            invokeWrapper.setParamTypeNames(paramTypeNames);
            invokeWrapper.setParamJsonObjects(paramJsonObjects);

        }


        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> request = new HttpEntity<>(objectMapper.writeValueAsString(invokeWrapper), headers);

        ResultWrapper resultWrapper = restTemplate.postForObject(url, request, ResultWrapper.class);

        //断言一定不能为空
        if (resultWrapper.getExceptionClassName() != null) {
            Class<?> aClass;
            try {
                aClass = Class.forName(resultWrapper.getExceptionClassName());
            } catch (Exception e) {
                aClass = RuntimeException.class;
            }

            if (resultWrapper.getExceptionMsg() == null) {
                throw (Exception) aClass.newInstance();
            } else {
                Constructor<?> constructor = aClass.getConstructor(String.class);
                throw (Exception) constructor.newInstance(resultWrapper.getExceptionMsg());
            }

        }
        Class<?> returnType = method.getReturnType();
        return objectMapper.readValue(resultWrapper.getReturnResult(), SimpleType.constructUnsafe(returnType));
    }


}
